#include <iostream>
#include <sys/select.h>
#include <string>
#include <cstring>
#include <unistd.h>
#include "Sock.hpp"

#define NUM (sizeof(fd_set) * 8)
int fd_array[NUM]; // 内容>=0, 合法的fd,如果是-1，该位置没有fd

void Usage(std::string proc)
{
    std::cout << "Usage\n\t" << proc << " port" << std::endl;
}

// ./SelectServer 8080
int main(int argc, char *argv[])
{
    // std::cout << sizeof(fd_set) * 8 << std::endl;// fd_set的大小
    if (argc != 2)
    {
        Usage(argv[0]);
        exit(1);
    }

    int listen_sock = Sock::Socket();
    uint16_t port = static_cast<uint16_t>(atoi(argv[1]));
    Sock::Bind(listen_sock, port);
    Sock::Listen(listen_sock);
    // accept: 不应该，accept的本质叫做通过listen_sock获取新链接
    //         前提是listen_sock上面有新链接，accept怎么知道有新链接呢？？
    //         不知道！！！accept阻塞式等待
    //         站在多路转接的视角，我们认为，链接到来，对于listen_sock,就是读事件就绪！！！
    //         对于所有的服务器，最开始的时候，只有listen_sock

    // 事件循环
    fd_set fds;
    for (int i = 1; i < NUM; ++i)
    {
        fd_array[i] = -1;
    }
    fd_array[0] = listen_sock;
    int max_fd = listen_sock;

    for (;;)
    {
        FD_ZERO(&fds); //将位图全部清零
        for (int i = 0; i < NUM; ++i)
        {
            // 用户告诉内核，你要帮我关心哪些fd上的读事件已经就绪
            if (fd_array[i] == -1)
                continue;
            FD_SET(fd_array[i], &fds);
            if (fd_array[i] > max_fd)
            {
                max_fd = fd_array[i];
            }
        }
        struct timeval timeout = {5, 0};
        // int n = select(max_fd + 1, &fds, nullptr, nullptr, &timeout);
        int n = select(max_fd + 1, &fds, nullptr, nullptr, nullptr); // 阻塞式等待多个文件描述符,等待链接，等待数据发送
        switch (n)
        {
        case -1:
            std::cerr << "select failed!" << std::endl;
            break;
        // 时间超出
        case 0:
            std::cout << "select timeout" << std::endl;
            break;
        default:
            std::cout << "select success, 有对应的fd已经就绪了" << std::endl;
            // 查找哪些fd就绪了
            for (int i = 0; i < NUM; ++i)
            { // for 1
                if (fd_array[i] == -1)
                    continue;
                // 判断是否是合法的fd
                else if (FD_ISSET(fd_array[i], &fds))
                {
                    if (fd_array[i] == listen_sock)
                    {
                        std::cout << "listen_sock:" << listen_sock << " 正在获取新链接" << std::endl;
                        int new_sock = Sock::Accept(listen_sock);
                        // 获取成功
                        // recv,read了呢？绝对不能！
                        // 新链接到来，不意味着有数据到来！！什么时候数据到来呢?不知道
                        // 可是，谁可以最清楚的知道那些fd，上面可以读取了？select！
                        // 无法直接将fd设置进select，但是，好在我们有fd_array[]!
                        if (new_sock >= 0)
                        {
                            std::cout << "listen_sock:" << listen_sock << " 获取新链接成功 new_sock:"
                                      << new_sock << std::endl;
                            int pos = 1;
                            // 将合法的fd设置进fd_array数组
                            for (; pos < NUM; pos++)
                            {
                                if (fd_array[pos] == -1)
                                    break;
                            }
                            if (pos < NUM)
                            {
                                // 成功找到了一个位置
                                std::cout << "新链接new_sock:" << new_sock << "已经被添加到fd_array[" << pos << "]中" << std::endl;
                                fd_array[pos] = new_sock;
                            }
                            else
                            {
                                std::cout << "服务器已满,关闭套接字" << std::endl;
                                close(new_sock);
                            }
                        }
                    }

                    else
                    {
                        //正常读取
                        // 普通的sock，读事件就绪啦！
                        // 可以进行读取啦，recv，read
                        // 可是，本次读取就一定能读完吗？读完，就一定没有所谓的数据包粘包问题吗？
                        // 但是，我们今天没法解决！我们今天没有场景！仅仅用来测试
                        std::cout << "new_sock: " << fd_array[i] << " 上面有普通读取" << std::endl;
                        char recvbuffer[1024];
                        ssize_t s = read(fd_array[i], recvbuffer, sizeof(recvbuffer));
                        if (s > 0)
                        {
                            recvbuffer[s] = '\0';
                            std::cout << "client " << fd_array[i] << " " << recvbuffer << std::endl;
                        }
                        else if (s == 0)
                        {
                            std::cout << "client已经关闭,服务端关闭new_sock" << std::endl;
                            close(fd_array[i]);
                            fd_array[i] = -1;
                        }
                        else
                        {
                            std::cout << "读取失败,服务端关闭new_sock" << std::endl;
                            close(fd_array[i]);
                            fd_array[i] = -1;
                        }
                    }
                }
            } // end of for 1
            break;
        }
    }
    return 0;
}